package com.bwie.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bwie.config.KafkaConfig;
import com.bwie.es.EsProd;
import com.bwie.mapper.TbDictMapper;
import com.bwie.mapper.TbProdImageMapper;
import com.bwie.pojo.TbDict;
import com.bwie.pojo.TbProd;
import com.bwie.pojo.TbProdImage;
import com.bwie.pojo.TbUser;
import com.bwie.service.IUserService;
import com.bwie.service.TbProdService;
import com.bwie.mapper.TbProdMapper;
import com.bwie.status.ProdEnum;
import com.bwie.utils.MobileMessageUtil;
import com.bwie.utils.R;
import com.bwie.vo.PageInfoVo;
import com.bwie.vo.TbProdVo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
* @author andy
* @description 针对表【tb_prod(商品表)】的数据库操作Service实现
* @createDate 2025-03-25 13:58:43
*/
@Service
public class TbProdServiceImpl extends ServiceImpl<TbProdMapper, TbProd>
    implements TbProdService{

    @Autowired
    TbProdMapper tbProdMapper;

    @Autowired
    TbProdImageMapper tbProdImageMapper;

    @Autowired
    TbDictMapper tbDictMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    HttpServletRequest request;

    @Autowired
    KafkaTemplate kafkaTemplate;

    @Autowired
    IUserService iUserService;

    @Autowired
    JavaMailSender javaMailSender;
    @Override
    public R getProdList(PageInfoVo pageInfoVo) {

        //--1 构造分页参数
        Page<TbProd> page = new Page<>(pageInfoVo.getPageNum(), pageInfoVo.getPageSize());

        //--2 获取分页数据
        QueryWrapper<TbProd> wrapper = new QueryWrapper<>();
        wrapper.lambda().orderByDesc(TbProd::getProdId);

        Page<TbProd> tbProdPage = tbProdMapper.selectPage(page, wrapper);

        //--3 单独查图片
        for (TbProd record : tbProdPage.getRecords()) {
            List<TbProdImage> tbProdImages = tbProdImageMapper.selectList(
                    new QueryWrapper<TbProdImage>().lambda()
                            .eq(TbProdImage::getProdId, record.getProdId())
            );

            ArrayList<String> imageList = new ArrayList<>();
            for (TbProdImage tbProdImage : tbProdImages) {
                imageList.add(tbProdImage.getImageUrl());
            }

            record.setImageList(imageList);
        }

        return R.OK(tbProdPage);
    }

    @Override
    public R getDictList(Integer id) {

        //-- 判断缓存中是否存在
        String key = "dict_" + id;
        Boolean hasKey = stringRedisTemplate.hasKey(key);
        if(hasKey) {
            System.out.println("从缓存中读取");
            String s = stringRedisTemplate.opsForValue().get(key);
            List<TbDict> tbDicts = JSON.parseArray(s, TbDict.class);
            return R.OK(tbDicts);
        }


        //-- 从数据库中读取
        System.out.println("从数据库中读取");
        List<TbDict> tbDicts = tbDictMapper.selectList(
                new QueryWrapper<TbDict>().lambda()
                        .eq(TbDict::getDictType, id)
                        .orderByAsc(TbDict::getDictValue)
        );
        stringRedisTemplate.opsForValue().set(key, JSON.toJSONString(tbDicts));

        return R.OK(tbDicts);
    }

    @Override
    public R getProdListFromEs(PageInfoVo pageInfoVo) {
        //-- 构造分页参数
        Integer pageNum = 0;
        if(pageInfoVo.getPageNum() > 0) {
            pageNum = pageInfoVo.getPageNum() -1 ;
        }
        PageRequest pageRequest = PageRequest.of(pageNum, pageInfoVo.getPageSize());

        //-- 构造多条件查询
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        if(StringUtils.isNotEmpty(pageInfoVo.getKeyWord())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("prodName", pageInfoVo.getKeyWord()));
        }

        //-- 价格区间查询
        if(pageInfoVo.getMinValue() != null) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("prodPrice").gte(pageInfoVo.getMinValue()));
        }
        if(pageInfoVo.getMaxValue() != null) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("prodPrice").lte(pageInfoVo.getMaxValue()));
        }

        //-- 时间区间查询
        if(StringUtils.isNotEmpty(pageInfoVo.getBeginTime())) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime")
                    .format("yyyy-MM-dd HH:mm:ss")
                    .timeZone("GMT+8")
                    .gte(pageInfoVo.getBeginTime())
            );
        }
        if(StringUtils.isNotEmpty(pageInfoVo.getEndTime())) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime")
                    .format("yyyy-MM-dd HH:mm:ss")
                    .timeZone("GMT+8")
                    .lte(pageInfoVo.getEndTime())
            );
        }

        // 仅获取待审核的商品
        boolQueryBuilder.must(QueryBuilders.termQuery("prodStatus", 1));

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("prodName").preTags("<font color='red'>").postTags("</font>");

        // 排序
        FieldSortBuilder prodPriceOrder = SortBuilders.fieldSort("prodId").order(SortOrder.DESC);

        //-- 开始查询
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder)
                .withPageable(pageRequest)
                .withHighlightBuilder(highlightBuilder)
                .withSort(prodPriceOrder);
        SearchHits<EsProd> searchHits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), EsProd.class);

        //-- 读取返回结果
        long total = searchHits.getTotalHits();
        ArrayList<EsProd> records = new ArrayList<>();

        for (SearchHit<EsProd> searchHit : searchHits.getSearchHits()) {
            EsProd esProd = searchHit.getContent();

            // 获取高亮
            List<String> prodNameList = searchHit.getHighlightField("prodName");
            if(prodNameList != null && prodNameList.size() > 0) {
                esProd.setProdName(prodNameList.get(0));
            }

            records.add(esProd);
        }

        HashMap<String, Object> map = new HashMap<>();
        map.put("total", total);
        map.put("records", records);

        return R.OK(map);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R addProd(TbProd tbProd) {
        // 获取商家ID
        String token = request.getHeader("token");
        if(token == null) {
            return R.ERROR(500, "请先登录");
        }

        Claims body = Jwts.parser().setSigningKey("123456").parseClaimsJws(token).getBody();
        Integer userId = body.get("userId", Integer.class);


        // 判断商品是否存在
        TbProd prod = tbProdMapper.selectOne(
                new QueryWrapper<TbProd>().lambda()
                        .eq(TbProd::getProdName, tbProd.getProdName())
        );
        if(prod != null) {
            return R.ERROR(500, "商品已存在");
        }

        // 保存商品
        tbProd.setProdStatus(ProdEnum.WAIT.getCode());
        tbProd.setCreateTime(new Date());
        tbProd.setUserId(userId);

        tbProdMapper.insert(tbProd);

        // 添加商品的图片
        if(tbProd.getImageList() != null && tbProd.getImageList().size() > 0) {
            for (String image : tbProd.getImageList()) {
                TbProdImage tbProdImage = new TbProdImage();
                tbProdImage.setProdId(tbProd.getProdId());
                tbProdImage.setImageUrl(image);

                tbProdImageMapper.insert(tbProdImage);
            }
        }

        return R.OK();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateProd(TbProd tbProd) {
        //-- 判断商品是否存在
        TbProd prod = tbProdMapper.selectById(tbProd.getProdId());
        if(prod == null) {
            return R.ERROR(500, "商品不存在");
        }

        //-- 修改商品信息
        tbProdMapper.updateById(tbProd);

        //-- 修改商品的图片
        tbProdImageMapper.delete(new QueryWrapper<TbProdImage>().lambda()
                .eq(TbProdImage::getProdId, tbProd.getProdId())
        );
        if(tbProd.getImageList() != null && tbProd.getImageList().size() > 0) {
            for (String image : tbProd.getImageList()) {
                TbProdImage tbProdImage = new TbProdImage();
                tbProdImage.setProdId(tbProd.getProdId());
                tbProdImage.setImageUrl(image);

                tbProdImageMapper.insert(tbProdImage);
            }
        }

        return R.OK();
    }

    @Override
    public R deleteProd(Integer prodId) {
        // 判断商品是否存在
        TbProd tbProd = tbProdMapper.selectById(prodId);
        if(tbProd == null) {
            return R.ERROR(500, "商品不存在");
        }

        // 删除商品
        tbProdMapper.deleteById(prodId);

        return R.OK();
    }

    @Override
    public R applyProdOnline(Integer prodId) {

        TbProd tbProd = tbProdMapper.selectById(prodId);
        if(tbProd == null) {
            return R.ERROR(500, "商品不存在");
        }

        tbProd.setProdStatus(ProdEnum.WAIT_CHECK.getCode());
        tbProdMapper.updateById(tbProd);

        // 实时同步
        tbProd = tbProdMapper.selectById(prodId);
        EsProd esProd = BeanUtil.toBean(tbProd, EsProd.class);
        elasticsearchRestTemplate.save(esProd);


        return R.OK();
    }

    @Override
    public R checkProd(TbProd tbProd) {
        //-- 判断商品是否存在
        TbProd prod = tbProdMapper.selectById(tbProd.getProdId());
        if(prod == null) {
            return R.ERROR(500, "商品不存在");
        }

        //-- 如果是驳回，判断是否输入了驳回原因
        if(tbProd.getProdStatus() == 3) {
            if(StringUtils.isEmpty(tbProd.getReason())) {
                return R.ERROR(500, "驳回原因不能为空");
            }
        }

        //-- 产品审核
        prod.setProdStatus(tbProd.getProdStatus());
        prod.setReason(tbProd.getReason());
        tbProdMapper.updateById(prod);

        //-- ES实时同步
        EsProd esProd = BeanUtil.toBean(prod, EsProd.class);
        elasticsearchRestTemplate.save(esProd);

        //-- （异步）审核通过了发送短信，审核不通过发送邮件
        String message = JSON.toJSONString(prod);
        kafkaTemplate.send(KafkaConfig.TOPIC_NAME, message);
        System.out.println("发送kafka消息：" + message);

        return R.OK();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R sucProdStock(List<TbProdVo> prodVoList) {

        for (TbProdVo tbProdVo : prodVoList) {
            TbProd tbProd = tbProdMapper.selectById(tbProdVo.getProdId());
            if(tbProd.getProdStock() < tbProdVo.getBuyNum()) {
                //return R.ERROR(500, "库存不足");
                throw new RuntimeException("库存不足");
            }

            tbProd.setProdStock(tbProd.getProdStock() - tbProdVo.getBuyNum());
            tbProdMapper.updateById(tbProd);
        }

        return R.OK();
    }


    @KafkaListener(topics = KafkaConfig.TOPIC_NAME)
    public void recvCheckMessage(String message, Acknowledgment acknowledgment) {
        //-- 接收消息
        System.out.println("接收到消息："+message);
        TbProd tbProd = JSON.parseObject(message, TbProd.class);

        // 如果是审核通过，发送短信
        if(tbProd.getProdStatus() == 2) {
            System.out.println("发送短信");

            Integer userId = tbProd.getUserId();
            R r = iUserService.getUserById(userId);
            if(r.getCode() != 200) {
                System.out.println("查询用户信息失败");
                acknowledgment.acknowledge();
                return;
            }

            String s = JSON.toJSONString(r.getData());
            TbUser tbUser = JSON.parseObject(s, TbUser.class);

            String body = tbProd.getProdName() + " 审核结果：已通过";
            MobileMessageUtil.sendMobileMessage(tbUser.getUserMobile(), body);
            System.out.println("发送短信：" + body);

            acknowledgment.acknowledge();
            return;
        }

        // 如果是审核不通过，发送邮件
        if(tbProd.getProdStatus() == 3) {
            System.out.println("发送邮件");
            try {
                MimeMessage mimeMessage = javaMailSender.createMimeMessage();
                MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);

                mimeMessageHelper.setFrom("1990609643@qq.com");
                mimeMessageHelper.setTo("3350996729@qq.com");
                mimeMessageHelper.setSubject(tbProd.getProdName()+" 商品审核结果");

                String body = tbProd.getProdName() + " 审核结果：被驳回。" + tbProd.getReason();
                mimeMessageHelper.setText(body);

                javaMailSender.send(mimeMessage);
            } catch (MessagingException e) {
//                throw new RuntimeException(e);
                e.printStackTrace();
                return;
            }
        }

        //
        ArrayList<EsProd> esProds = new ArrayList<>();
        elasticsearchRestTemplate.save(esProds);

    }
}




